package com.dews.course.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dews.common.utils.SecurityUtils;
import com.dews.common.utils.excel.ExcelSelectRo;
import com.dews.course.convert.StudentScoreConvert;
import com.dews.course.domain.entity.StudentScore;
import com.dews.course.domain.ro.StudentScoreDetailRo;
import com.dews.course.domain.ro.StudentScoreRo;
import com.dews.course.domain.vo.StudentScoreStatisticsDataVo;
import com.dews.course.domain.vo.StudentScoreVo;
import com.dews.course.mapper.StudentScoreMapper;
import com.dews.course.service.IStudentScoreService;
import com.dews.information.domain.entity.StudentInfo;
import com.dews.information.domain.entity.Subject;
import com.dews.information.domain.entity.SubjectCourse;
import com.dews.information.mapper.StudentInfoMapper;
import com.dews.information.mapper.SubjectCourseMapper;
import com.dews.information.mapper.SubjectMapper;
import com.dews.system.domain.SysDept;
import com.dews.system.domain.SysDictData;
import com.dews.system.domain.SysUser;
import com.dews.system.mapper.SysDeptMapper;
import com.dews.system.mapper.SysDictDataMapper;
import com.dews.system.mapper.SysUserMapper;
import com.dews.train.domain.entity.ProgramTeachPlan;
import com.dews.train.domain.entity.TrainingProgram;
import com.dews.train.mapper.ProgramTeachPlanMapper;
import com.dews.train.mapper.TrainingProgramMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 学生课程成绩Service业务层处理
 *
 * @author qingfeng
 * @date 2025-01-21
 */
@Service
@Slf4j
public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreMapper, StudentScore> implements IStudentScoreService {

    private final SysUserMapper sysUserMapper;
    private StudentScoreMapper studentScoreMapper;
    private SysDeptMapper sysDeptMapper;
    private SubjectMapper subjectMapper;
    private SubjectCourseMapper subjectCourseMapper;
    private SysDictDataMapper sysDictDataMapper;
    private StudentInfoMapper studentInfoMapper;
    private TrainingProgramMapper trainingProgramMapper;
    private ProgramTeachPlanMapper programTeachPlanMapper;

    @Autowired
    public StudentScoreServiceImpl(StudentScoreMapper studentScoreMapper,
                                   SysDeptMapper sysDeptMapper,
                                   SubjectMapper subjectMapper,
                                   SubjectCourseMapper subjectCourseMapper,
                                   SysDictDataMapper sysDictDataMapper,
                                   SysUserMapper sysUserMapper,
                                   StudentInfoMapper studentInfoMapper,
                                   TrainingProgramMapper trainingProgramMapper,
                                   ProgramTeachPlanMapper programTeachPlanMapper) {
        this.studentScoreMapper = studentScoreMapper;
        this.sysDeptMapper = sysDeptMapper;
        this.subjectMapper = subjectMapper;
        this.subjectCourseMapper = subjectCourseMapper;
        this.sysDictDataMapper = sysDictDataMapper;
        this.sysUserMapper = sysUserMapper;
        this.studentInfoMapper = studentInfoMapper;
        this.trainingProgramMapper = trainingProgramMapper;
        this.programTeachPlanMapper = programTeachPlanMapper;
    }

    @Override
    public List<ExcelSelectRo> selectDropDown() {
        List<ExcelSelectRo> excelSelectRoList = new ArrayList<>();

        // 查询所有的专业信息
        List<Subject> subjectList = subjectMapper.selectList(Wrappers.lambdaQuery(Subject.class).eq(Subject::getIsEnable, 1));
//        excelSelectRoList.add(ExcelSelectRo.builder().index(3).dapDropDownStr(subjectList.stream().map(Subject::getSubjectName).distinct().toArray(String[]::new)).build());
//        excelSelectRoList.add(ExcelSelectRo.builder().index(4).dapDropDownStr(subjectList.stream().map(Subject::getSubjectCode).distinct().toArray(String[]::new)).build());

        Set<Long> subjectIdSet = subjectList.stream().map(Subject::getDepId).collect(Collectors.toSet());
        // 查询学院信息
        List<SysDept> sysDeptList = Collections.emptyList();
        // 查询所有专业课程
        List<SubjectCourse> subjectCourseList = Collections.emptyList();
        if (CollUtil.isNotEmpty(subjectIdSet)) {
            // 学院
            sysDeptList = sysDeptMapper.selectList(
                    Wrappers.lambdaQuery(SysDept.class)
                            .in(SysDept::getDeptId, subjectIdSet)
            );
            // 专业课程
            subjectCourseList = subjectCourseMapper.selectList(
                    Wrappers.lambdaQuery(SubjectCourse.class)
                            .in(SubjectCourse::getSubjectId, subjectList.stream().map(Subject::getId).collect(Collectors.toSet()))
            );
        }
//        excelSelectRoList.add(ExcelSelectRo.builder().index(2).dapDropDownStr(sysDeptList.stream().map(SysDept::getDeptName).distinct().toArray(String[]::new)).build());
        excelSelectRoList.add(ExcelSelectRo.builder().index(3).dapDropDownStr(subjectCourseList.stream().map(SubjectCourse::getCourseName).distinct().toArray(String[]::new)).build());
        excelSelectRoList.add(ExcelSelectRo.builder().index(2).dapDropDownStr(subjectCourseList.stream().map(SubjectCourse::getCourseCode).distinct().toArray(String[]::new)).build());

        // 封装学期
        List<SysDictData> selectDictDataList = sysDictDataMapper.selectDictDataList(SysDictData.builder().dictType("semester").build());
        excelSelectRoList.add(ExcelSelectRo.builder().index(5).dapDropDownStr(selectDictDataList.stream().map(SysDictData::getDictLabel).distinct().toArray(String[]::new)).build());

        return excelSelectRoList;
    }

    /**
     * 导入学生成绩
     *
     * @param studentScoreRoList
     * @param errorList
     * @param courseCode
     */
    @Override
    public void importData(ArrayList<StudentScoreRo> studentScoreRoList, ArrayList<StudentScoreRo> errorList, String courseCode) {
        // 1. 根据学号查询存在的用户
        Map<String, SysUser> sysUserMap = sysUserMapper.selectList(
                        Wrappers.lambdaQuery(SysUser.class)
                                .in(SysUser::getUserName, studentScoreRoList.stream().map(StudentScoreRo::getStudentNo).filter(StrUtil::isNotBlank).distinct().toList())
                ).stream()
                .collect(Collectors.toMap(
                        SysUser::getUserName,
                        Function.identity()
                ));
        // 2. 查询学生学籍信息
        Map<String, StudentInfo> studentInfoMap = studentInfoMapper.selectList(
                        Wrappers.lambdaQuery(StudentInfo.class)
                                .in(StudentInfo::getStudentNum, studentScoreRoList.stream().map(StudentScoreRo::getStudentNo).filter(StrUtil::isNotBlank).collect(Collectors.toSet()))
                ).stream()
                .collect(Collectors.toMap(
                        StudentInfo::getStudentNum,
                        Function.identity()
                ));

        // 3. 根据专业课程代码查询专业课程信息
        Map<String, SubjectCourse> subjectCourseMap = subjectCourseMapper.selectList(
                        Wrappers.lambdaQuery(SubjectCourse.class)
                                .in(SubjectCourse::getCourseCode, StrUtil.isNotBlank(courseCode) ? Collections.singletonList(courseCode) : studentScoreRoList.stream().map(StudentScoreRo::getCourseCode).filter(StrUtil::isNotBlank).distinct().toList())
                ).stream()
                .collect(Collectors.toMap(
                        SubjectCourse::getCourseCode,
                        Function.identity()
                ));

        // 封装修读方案计划
        List<TrainingProgram> trainingProgramList = trainingProgramMapper.selectList(
                Wrappers.lambdaQuery(TrainingProgram.class)
                        .in(CollUtil.isNotEmpty(studentInfoMap), TrainingProgram::getDepId, studentInfoMap.values().stream().map(StudentInfo::getDepId).collect(Collectors.toSet()))
                        .in(CollUtil.isNotEmpty(studentInfoMap), TrainingProgram::getSubjectId, studentInfoMap.values().stream().map(StudentInfo::getSubjectId).collect(Collectors.toSet()))
                        .in(CollUtil.isNotEmpty(studentInfoMap), TrainingProgram::getGrade, studentInfoMap.values().stream().map(StudentInfo::getGrade).collect(Collectors.toSet()))
                        .eq(TrainingProgram::getIsEnable, 1)
        );
        Map<String, List<ProgramTeachPlan>> teachPlanMap;
        if (CollUtil.isNotEmpty(trainingProgramList)) {
            Map<Integer, List<ProgramTeachPlan>> programTeachPlanMap = programTeachPlanMapper.selectList(
                            Wrappers.lambdaQuery(ProgramTeachPlan.class)
                                    .in(ProgramTeachPlan::getProgramId, trainingProgramList.stream().map(TrainingProgram::getId).collect(Collectors.toSet()))
                    ).stream()
                    .collect(Collectors.groupingBy(ProgramTeachPlan::getProgramId));

            teachPlanMap = trainingProgramList.stream()
                    .collect(Collectors.toMap(
                            t -> t.getDepId().toString() + t.getSubjectId() + t.getGrade(),
                            t -> programTeachPlanMap.get(t.getId())
                    ));
        } else {
            teachPlanMap = Collections.emptyMap();
        }


        // 处理数据
        studentScoreRoList.forEach(v -> {
            try {
                if (sysUserMap.containsKey(v.getStudentNo()) && studentInfoMap.containsKey(v.getStudentNo()) && subjectCourseMap.containsKey(v.getCourseCode())) {
                    StudentInfo studentInfo = studentInfoMap.get(v.getStudentNo());
                    StudentScore studentScore = StudentScore.builder()
                            .userId(sysUserMap.get(v.getStudentNo()).getUserId())
                            .depId(studentInfo.getDepId())
                            .subjectId(studentInfo.getSubjectId())
                            .subjectCourseId(subjectCourseMap.get(StrUtil.isNotBlank(courseCode) ? courseCode : v.getCourseCode()).getId())
                            .teacherId(v.getTeacherId())
                            .semester(v.getSemester())
                            .score(v.getScore())
                            .createUserId(SecurityUtils.getUserId())
                            .build();
                    // 先查询该成绩是都存在
                    StudentScore score = studentScoreMapper.selectOne(
                            Wrappers.lambdaQuery(StudentScore.class)
                                    .eq(StudentScore::getUserId, studentScore.getUserId())
                                    .eq(StudentScore::getDepId, studentScore.getDepId())
                                    .eq(StudentScore::getSubjectId, studentScore.getSubjectId())
                                    .eq(StudentScore::getSubjectCourseId, studentScore.getSubjectCourseId())
                                    .eq(StudentScore::getTeacherId, studentScore.getTeacherId())
                                    .eq(StudentScore::getSemester, studentScore.getSemester())
                    );
                    if (ObjectUtil.isNotNull(score)) {
                        studentScore.setId(score.getId());
                    }

                    // 需要计算学分、绩点
                    teachPlanMap.getOrDefault(studentInfo.getDepId().toString() + studentInfo.getSubjectId() + studentInfo.getGrade(), Collections.emptyList())
                            .stream()
                            .filter(p -> p.getCourseId().equals(studentScore.getSubjectCourseId()))
                            .findFirst()
                            .ifPresent(p -> {
                                // 教学计划存在，判断成绩是否及格
                                if (studentScore.getScore() >= 60) {
                                    // 赋值学分
                                    studentScore.setCredits(p.getCredit());
                                    // 只计算必修的
//                                    if (StudyRequireEnum.COMPULSORY.getCode().equals(p.getStudyRequire())) {
                                        // 计算绩点 = (成绩 - 60) / 10 ，绩点只能包含0,0.5,1,1,5 ,不满0.5的直接舍弃
                                        studentScore.setGradePoint(calculateGPA(studentScore.getScore()));
//                                    }
                                }
                                // 添加上修读要求
                                studentScore.setStudyRequire(p.getStudyRequire());
                            });
                    this.saveOrUpdate(studentScore);
                } else {
                    errorList.add(v);
                }
            } catch (Exception e) {
                log.error("导入失败：{}", e.getMessage());
                errorList.add(v);
            }
        });

    }

    /**
     * 计算绩点 = (成绩 - 60) / 10 ，绩点只能包含0,0.5,1,1,5 ,不满0.5的直接舍弃
     * @param score
     * @return
     */
    private double calculateGPA(double score) {
        // 计算初步绩点
        double gpa = (score - 60) / 10.0;

        // 如果绩点小于0，直接返回0
        if (gpa < 0) {
            return 0;
        }

        // 舍弃不满0.5的部分
        return Math.floor(gpa * 2) / 2;
    }

    /**
     * 专业课程点击详情查询课程下的学生成绩信息
     *
     * @param courseId
     * @return
     */
    @Override
    public List<StudentScoreDetailRo> selectStudentScoreByCourseId(Integer courseId) {
        return getStudentScoreDetails(courseId, null);
    }

    /**
     * 学生查询课程成绩信息
     *
     * @return {@link List }<{@link StudentScoreDetailRo }>
     */
    @Override
    public List<StudentScoreDetailRo> stuScoreDetail() {
        return getStudentScoreDetails(null, SecurityUtils.getUserId());
    }

    private List<StudentScoreDetailRo> getStudentScoreDetails(Integer courseId, Long userId) {
        // 查询学生分数列表
        List<StudentScore> studentScoreList = baseMapper.selectList(
                Wrappers.lambdaQuery(StudentScore.class)
                        .eq(ObjectUtil.isNotNull(courseId), StudentScore::getSubjectCourseId, courseId)
                        .eq(ObjectUtil.isNotNull(userId), StudentScore::getUserId, userId)
        );
        if (CollUtil.isEmpty(studentScoreList)) {
            return Collections.emptyList();
        }

        // 查询用户信息
        Set<Long> userAndTeacherIds = studentScoreList.stream()
                .flatMap(s -> Stream.of(s.getUserId(), s.getTeacherId()))
                .collect(Collectors.toSet());

        Map<Long, SysUser> sysUserMap = sysUserMapper.selectBatchIds(userAndTeacherIds)
                .stream()
                .collect(Collectors.toMap(SysUser::getUserId, Function.identity()));

        // 查询部门学院信息
        Map<Long, String> depMap = sysDeptMapper.selectBatchIds(
                        studentScoreList.stream().map(StudentScore::getDepId).collect(Collectors.toSet())
                ).stream()
                .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName));

        // 查询专业信息
        Map<Integer, String> subjectMap = subjectMapper.selectBatchIds(
                        studentScoreList.stream().map(StudentScore::getSubjectId).collect(Collectors.toSet())
                ).stream()
                .collect(Collectors.toMap(Subject::getId, Subject::getSubjectName));

        // 查询专业课程信息
        Map<Integer, String> subjectCourseMap = subjectCourseMapper.selectBatchIds(
                        studentScoreList.stream().map(StudentScore::getSubjectCourseId).collect(Collectors.toSet())
                ).stream()
                .collect(Collectors.toMap(SubjectCourse::getId, SubjectCourse::getCourseName));

        ArrayList<StudentScoreDetailRo> studentScoreDetailRos = new ArrayList<>();

        // 封装数据返回
        List<StudentScoreVo> studentScoreVoList = studentScoreList.stream()
                .map(s -> {
                    StudentScoreVo studentScoreVo = StudentScoreConvert.INSTANCE.convert(s);
                    SysUser studentUser = sysUserMap.getOrDefault(s.getUserId(), new SysUser());
                    SysUser teacherUser = sysUserMap.getOrDefault(s.getTeacherId(), new SysUser());
                    studentScoreVo.setStudentNum(studentUser.getUserName());
                    studentScoreVo.setTeacherName(teacherUser.getNickName());
                    studentScoreVo.setDepName(depMap.getOrDefault(s.getDepId(), "未知"));
                    studentScoreVo.setSubjectName(subjectMap.getOrDefault(s.getSubjectId(), "未知"));
                    studentScoreVo.setSubjectCourseName(subjectCourseMap.getOrDefault(s.getSubjectCourseId(), "未知"));
                    studentScoreVo.setStudyRequire(s.getStudyRequire());
                    studentScoreVo.setCredits(s.getCredits());
                    studentScoreVo.setGradePoint(s.getGradePoint());
                    return studentScoreVo;
                })
                .collect(Collectors.toList());

        // 根据教师ID分组添加统计信息
        if (ObjectUtil.isNotNull(courseId)) {
            // 添加全部学生的统计信息
            studentScoreDetailRos.add(createStudentScoreDetailRo("0", "全部", studentScoreVoList));
            studentScoreVoList.stream()
                    .collect(Collectors.groupingBy(StudentScoreVo::getTeacherId))
                    .values()
                    .forEach(e -> studentScoreDetailRos.add(createStudentScoreDetailRo(e.get(0).getTeacherId().toString(), e.get(0).getTeacherName(), e)));
        } else {
            // 添加全部学生的统计信息
            studentScoreDetailRos.add(createStudentScoreDetailRo("全部", "全部", studentScoreVoList));
            // 根据学期分组添加统计信息
            studentScoreVoList.stream()
                    .collect(Collectors.groupingBy(StudentScoreVo::getSemester))
                    .values()
                    .forEach(e -> studentScoreDetailRos.add(createStudentScoreDetailRo(e.get(0).getSemester(), e.get(0).getSemester(), e)));
        }
        return studentScoreDetailRos;
    }

    private StudentScoreDetailRo createStudentScoreDetailRo(String activeName, String label, List<StudentScoreVo> studentScoreVoList) {
        return StudentScoreDetailRo.builder()
                .activeName(activeName)
                .label(label)
                .studentScoreVoList(studentScoreVoList)
                .studentScoreStatisticsDataVo(
                        StudentScoreStatisticsDataVo.builder()
                                .totalStudentNum(studentScoreVoList.size())
                                .averageScore(studentScoreVoList.stream().mapToDouble(StudentScoreVo::getScore).average().orElse(0))
                                .failNum((int) studentScoreVoList.stream().filter(e -> e.getScore() < 60).count())
                                .excellentNum((int) studentScoreVoList.stream().filter(e -> e.getScore() >= 90).count())
                                .build()
                )
                .build();
    }


    /**
     * 查询学生课程成绩列表
     *
     * @param studentScoreVo 学生课程成绩
     * @return 学生课程成绩
     */
    @Override
    public List<StudentScoreVo> selectStudentScoreList(StudentScoreVo studentScoreVo) {
        return Collections.emptyList();
    }

    /**
     * 查询学生课程成绩
     *
     * @param id 学生课程成绩主键
     * @return 学生课程成绩
     */
    @Override
    public StudentScoreVo selectStudentScoreById(Integer id) {
        return null;
    }

    /**
     * 新增学生课程成绩
     *
     * @param studentScoreVo 学生课程成绩
     * @return 结果
     */
    @Override
    public int insertStudentScore(StudentScoreVo studentScoreVo) {
        StudentScore studentScore = StudentScoreConvert.INSTANCE.convert(studentScoreVo);


        return studentScoreMapper.insert(studentScore);

    }

    /**
     * 修改学生课程成绩
     *
     * @param studentScoreVo 学生课程成绩
     * @return 结果
     */
    @Override
    public int updateStudentScore(StudentScoreVo studentScoreVo) {
        StudentScore studentScore = StudentScoreConvert.INSTANCE.convert(studentScoreVo);
        return studentScoreMapper.updateById(studentScore);
    }

    /**
     * 批量删除学生课程成绩
     *
     * @param ids 需要删除的学生课程成绩主键
     * @return 结果
     */
    @Override
    public int deleteStudentScoreByIds(List<Integer> ids) {
        return studentScoreMapper.deleteBatchIds(ids);
    }


}
